<?php
class NoBusinessBase_Utility_Heartbeat
{
   const VERSION = "1.0.0";
   protected $mConnections = array();

   public  function __construct()
   {
   }

   public function __clone()
   {
      trigger_error('Clone is not allowed.', E_USER_ERROR);
   }

   public function __wakeup()
   {
      trigger_error('Unserializing is not allowed.', E_USER_ERROR);
   }


   public function __destruct()
   {
   }

/************************************************************************/
/* Interface                                                            */
/************************************************************************/

   public function addConnection($host, $port, $tls)
   {
      $pConnection = new NoBusinessBase_Tcp_Client($host, $port, $tls);
      if(!$pConnection->connected())
         return null;

      $id = strval($host) . strval($port);
      $this->mConnections[$id] = $pConnection;

      return $id;
   }

   public function disconnect($id)
   {
      if(!array_key_exists($id, $this->mConnections))
         return false;

      $connection = $this->mConnections[$id];
      $connection->close();
   }

   public function requestChannels($id)
   {
      if(!array_key_exists($id, $this->mConnections))
         return false;

      //Do a blocking read to get the version.
      $this->readFromConnection($id); 

      //Do a blocking read to get the channel list.
      $this->readFromConnection($id);

      $connection = $this->mConnections[$id];
      $connection->mChannelsRequested = true;
   }

   public function removeConnection($id)
   {
      if(!array_key_exists($id, $this->mConnections))
         return;

      $pConnection = $this->mConnections[$id];
      $pConnection->close();
      unset($this->mConnections[$id]);
   }

   public function readFromConnection($id)
   {
      if(!array_key_exists($id, $this->mConnections))
         return false;

      $connection = $this->mConnections[$id];

      //If not connected, continue;
      if(!$connection->connected())
         return false;

      //Check for data on the socket.
      $data = $connection->readData();

      //If there's no data, just continue onto the next one.
      if(!$data)
         return false;

      //Decode the JSON message.
      $msgJson = Zend_Json::decode($data["message"]);

      //If there was no valid message, return.
      if(!$msgJson)
         return false;

      //Check the channel of the data to see if the library should handle it.
      if($data["channel"] === 1)
      {
         $versionStr = $msgJson["version"];
         if(!$this->versionCheck($versionStr))
         {
            $this->removeConnection($id);
            return false;
         }
         return false;
      }

      if($data["channel"] === 2)
      {
         //$connection->fillChannelMap($msgJson);
         return false;
      }

      if($data["channel"] === 4)
         return $data["channel"];

      if($data["channel"] <= 5)
         return false;

      //Invoke the onMessageCallback for the connection.
      $packet = array("channel" => $data["channel"],
                      "message" => $msgJson
                     );


      return $packet;
   }

   public function setOnMessageCallback($id, $callback)
   {
      if(!array_key_exists($id, $this->mConnections))
         return;

      $pConnection = $this->mConnections[$id];
      $pConnection->setOnMessageCallback($callback);
   }

   public function subscribeTo($id, $channels)
   {
      if(!array_key_exists($id, $this->mConnections))
         return;

      $pConnection = $this->mConnections[$id];
      $pConnection->subscribeTo($channels);
      
   }

   public function writeToConnection($id, $channel, $msgJson)
   {
      if(!array_key_exists($id, $this->mConnections))
         return;

      $pConnection = $this->mConnections[$id];
      //$channelJson = array('channel' => $channel);
      //$data = array('header' => $channelJson, 'message' => $msgJson);
      $pConnection->sendData($channel, $msgJson);
   }

   public function ping($id)
   {
      if(!array_key_exists($id, $this->mConnections))
         return false;

      $pConnection = $this->mConnections[$id];
      if(!$pConnection->mChannelsRequested)
         $this->requestChannels($id);

      $channel = 4;
      $msgJson = array('p' => 'p');
      $pConnection->sendData($channel, $msgJson);
      return $this->readFromConnection($id);
   }

/************************************************************************/
/* Utilities                                                            */
/************************************************************************/

   private function versionCheck($versionStr)
   {
      if(!$versionStr)
         return false;

      $clientxyz = explode(".", self::VERSION);
      $versionxyz = explode(".", $versionStr);

      if(count($versionxyz) != 3 || count($clientxyz) != 3)
         return false;

      if($clientxyz[0] != $versionxyz[0] || $clientxyz[1] != $versionxyz[1])
         return false;

      return true;
   }
}

